//
//  MinFlexValue.swift
//  soapp_Example
//
//  Created by zhu xietong on 2018/2/27.
//  Copyright © 2018年 CocoaPods. All rights reserved.
//

import Foundation
import AsyncDisplayKit

prefix operator &-
public prefix func &-(value:FlexAlignItems) -> FlexAlignItems  {
    return value
}

prefix operator &+
public prefix func &+(value:FlexCenter) -> FlexCenter  {
    return value
}

prefix operator  &=
public prefix func &=(value:FlexAlignContent) -> FlexAlignContent  {
    return value
}

struct FlexValueKey
{
    static var width = "width"
    static var height = "height"
    static var space = "space"
}

extension ASInsetLayoutSpec{
    func setAtrributes(attribues:[Any]) {
        for one in attribues {
            if let grow = one as? _grow{
                self.style.flexGrow = CGFloat(grow.value)
            }
            if let shrink = one as? _shrink{
                self.style.flexShrink = CGFloat(shrink.value)
            }
        }
    }
}


public struct SpecCreate
{
    
    static func assin(rules:[Any],spec:ASLayoutSpec)->ASLayoutSpec
    {
        let _rules = SpecCreate.formatValues(values: rules)
        
        var element:ASLayoutSpec = spec
        
        for one in _rules
        {
            switch one{
            case .alignItems(_):
                break
            case .direction(_):
                break
            case .justifyContent(_):
                break
            case .space(_):
                break
            case .alignSelf(let align):
                element.style.alignSelf = align
                
            case .width(let width):
                switch width{
                case .max(value: let v, key: _):
                    element.style.maxWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    element.style.minWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    element.style.width = ASDimension(unit: .points, value: CGFloat(v))
                }
            case .height(let height):
                switch height{
                case .max(value: let v, key: _):
                    element.style.maxHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    element.style.minHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    element.style.height = ASDimension(unit: .points, value: CGFloat(v))
                }
                
            case .shrink(let shrink):
                element.style.flexShrink = CGFloat(shrink.doubleValue)
            case .grow(let grow):
                element.style.flexGrow = CGFloat(grow.doubleValue)
            case .wrap(_):
                break
            case .padding(let inset,let options):
                let insetSpec = ASInsetLayoutSpec(insets: inset, child: spec)
                insetSpec.setAtrributes(attribues: options)
                element = insetSpec
            case .alignContent(_):
                break
            case .specId(let idStr):
                element.sid = idStr
            }
        }
        return element
        
    }
    
    
    static func assin(rules:[Any],element:ASDisplayNode) ->_Spec
    {
        let _rules = SpecCreate.formatValues(values: rules)
        var spec = _Spec.node(element)
        
        for one in _rules
        {
            switch one{
            case .alignItems(_):
                break
            case .direction(_):
                break
            case .justifyContent(_):
                break
            case .space(_):
                break
            case .alignSelf(let align):
                element.style.alignSelf = align
                
            case .width(let width):
                switch width{
                case .max(value: let v, key: _):
                    element.style.maxWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    element.style.minWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    element.style.width = ASDimension(unit: .points, value: CGFloat(v))
                }
            case .height(let height):
                switch height{
                case .max(value: let v, key: _):
                    element.style.maxHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    element.style.minHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    element.style.height = ASDimension(unit: .points, value: CGFloat(v))
                }
                
            case .shrink(let shrink):
                element.style.flexShrink = CGFloat(shrink.doubleValue)
            case .grow(let grow):
                element.style.flexGrow = CGFloat(grow.doubleValue)
            case .wrap(_):
                break
            case .specId(let idStr):
                spec.__element.element.sid = idStr
            case .padding(let inset,let options):
                spec = _Spec.inset(options,inset, child: spec)
            case .alignContent(_):
                break
            }
            
        }
        return spec

    }
    
    static func newStack(rules:[Any],childs:[ASLayoutElement]) ->ASLayoutSpec
    {
        let _rules = SpecCreate.formatValues(values: rules)
        let stackSpec = ASStackLayoutSpec()
        
        //默认值
        stackSpec.flexWrap = .wrap
        stackSpec.alignContent = .start
        stackSpec.alignItems = .start
        stackSpec.children = childs
        
//        stackSpec.style.flexGrow = CGFloat(1)

        
        var spec:ASLayoutSpec = stackSpec
    
        
        for one in _rules
        {
            switch one{
            case .alignItems(let _alignItems):
                stackSpec.alignItems = _alignItems
            case .direction(let _direction):
                stackSpec.direction = _direction
            case .justifyContent(let _justifyContent):
                stackSpec.justifyContent = _justifyContent
            case .alignContent( let align):
                stackSpec.alignContent = align
            case .space(let space ):
                stackSpec.spacing = CGFloat(space)
            case .alignSelf(let align):
                stackSpec.style.alignSelf = align
                
            case .width(let width):
                switch width{
                case .max(value: let v, key: _):
                    stackSpec.style.maxWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    stackSpec.style.minWidth = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    stackSpec.style.width = ASDimension(unit: .points, value: CGFloat(v))
                }
            case .height(let height):
                switch height{
                case .max(value: let v, key: _):
                    stackSpec.style.maxHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .mini(value: let v, key: _):
                    stackSpec.style.minHeight = ASDimension(unit: .points, value: CGFloat(v))
                case .equal(value: let v, key: _):
                    stackSpec.style.height = ASDimension(unit: .points, value: CGFloat(v))
                }
            case .shrink(let shrink):
                stackSpec.style.flexShrink = CGFloat(shrink.doubleValue)
            case .grow(let grow):
                stackSpec.style.flexGrow = CGFloat(grow.doubleValue)
            case .wrap(let wrap):
                stackSpec.flexWrap = wrap
            case .specId(let idStr):
                stackSpec.sid = idStr
            case .padding(let inset,let options):
                let inset_ = UIEdgeInsets(top: inset.top, left: inset.left, bottom: inset.bottom, right: inset.right)
                let insetSpec = ASInsetLayoutSpec(insets: inset_, child: stackSpec)
                insetSpec.setAtrributes(attribues: options)
                spec = insetSpec

            }
        }
        return spec
    }
    
    
    
    public static func formatValues(values:[Any]) ->[FlexValue]
    {
        let orderValues = values.sorted { (v1,v2) -> Bool in
            if let _ = v1 as? [NumType]
            {
                return false
            }
            return true
        }
        
        var list = [FlexValue]()
        for value in orderValues{
            
            if let width = value as? NumType
            {
                list.append(FlexValue.width(DigitalValue.equal(value: width.doubleValue, key: FlexValueKey.width)))
                continue
            }
            
            let heigth = __isStringNumber(value: value)
            if heigth.0
            {
                
                list.append(FlexValue.height(DigitalValue.equal(value: heigth.1, key: FlexValueKey.height)))
                continue
            }
            
            if let strValue = value as? String
            {
                let _strValue = strValue.lowercased()
                if _strValue.hasPrefix("v")
                {
                    list.append(FlexValue.direction(.vertical))
                    let space = Double(_strValue.replacingOccurrences(of: "v|", with: "").CGFloatV)
                    
                    list.append(FlexValue.space(space))
                    continue
                }
                if _strValue.hasPrefix("h")
                {
                    list.append(FlexValue.direction(.horizontal))
                    let space = Double(_strValue.replacingOccurrences(of: "h|", with: "").CGFloatV) 
                    list.append(FlexValue.space(space))
                    continue
                }
                if _strValue.hasPrefix("id")
                {
                    let idStr = _strValue.replacingOccurrences(of: "id|", with: "")
                    if idStr.len > 0
                    {
                        list.append(FlexValue.specId(idStr))
                    }
                    continue
                }
            }
            
            if let dict = value as? [String:String]
            {
                if dict.keys.contains(">")
                {
                    let v = Double(dict[">"]!) ?? 0
                    list.append(FlexValue.height(DigitalValue.mini(value: v, key: FlexValueKey.height)));continue
                }
                if dict.keys.contains("<")
                {
                    let v = Double(dict["<"]!) ?? 0
                    list.append(FlexValue.height(DigitalValue.max(value: v, key: FlexValueKey.height)));continue
                }
            }
            
            if let dict = value as? [String:NumType]
            {
                if dict.keys.contains(">")
                {
                    let v = Double("\(dict[">"]!)") ?? 0
                    list.append(FlexValue.width(DigitalValue.mini(value: v, key: FlexValueKey.width)));continue
                }
                if dict.keys.contains("<")
                {
                    let v = Double("\(dict["<"]!)") ?? 0
                    list.append(FlexValue.width(DigitalValue.max(value: v, key: FlexValueKey.width)));continue
                }
            }
            
            
            
            if let padding_values = value as? [FlexInsetValue]
            {
                var padding:[NumType] = []
                var options:[Any] = []
                padding_values.forEach { (v) in
                    if let nv = v as? NumType{
                        padding.append(nv)
                    }else{
                        options.append(v)
                    }
                }
                
                
                let _padding:[CGFloat] = padding.map({ (one) -> CGFloat in
                    return one.doubleValue.cg_floatValue
                })
             
                var inset = UIEdgeInsets.zero
                switch _padding.count {
                case 2:
                    inset = UIEdgeInsets(top: _padding[0], left: _padding[1], bottom: _padding[0], right: _padding[1])
                case 4:
                    inset = UIEdgeInsets(top: _padding[0], left: _padding[1], bottom: _padding[2], right: _padding[3])
                case 1:
                    inset = UIEdgeInsets(top:_padding[0], left: _padding[0], bottom: _padding[0], right: _padding[0])
                case 3:
                    inset = UIEdgeInsets(top:_padding[0], left: _padding[1], bottom: _padding[2], right: _padding[1])
                case 0:
                    inset = UIEdgeInsets(top:0, left: 0, bottom: 0, right: 0)
                default:
                    inset = UIEdgeInsets(top:0, left: 0, bottom: 0, right: 0)
                }
                list.append(FlexValue.padding(inset, options: options))
                continue
            }
            
            if let wrap = value as? ASStackLayoutFlexWrap
            {
                list.append(FlexValue.wrap(wrap))
            }
            
            if let grow = value as? _grow{
                list.append(FlexValue.grow(grow.value))
            }
            
            if let shrink = value as? _shrink{
                list.append(FlexValue.shrink(shrink.value))
            }
            
            if let justfy = value as? _justfy
            {
                list.append(FlexValue.justifyContent(justfy.value.value))
            }
            
            if let align_items = value as? FlexAlignItems
            {
                list.append(FlexValue.alignItems(align_items.value))
            }
            
            if let align_self = value as? FlexItemAlignSelf
            {
                list.append(FlexValue.alignSelf(align_self.value))
            }
            
            if let align_content = value as? FlexAlignContent{
                list.append(FlexValue.alignContent(align_content.value))
            }
        }
        
        return list
        
    }

}


protocol FlexInsetValue {
    var doubleValue:Double{get}
}



protocol NumType:FlexInsetValue{}



extension Int:NumType{ var doubleValue:Double{get{return Double(self)}}}
extension Float:NumType{ var doubleValue:Double{get{return Double(self)}}}
extension CGFloat:NumType{ var doubleValue:Double{get{return Double(self)}}}
extension Double:NumType{ var doubleValue:Double{get{return self}}}



extension _shrink:FlexInsetValue{var doubleValue:Double{get{return value}}}
extension _grow:FlexInsetValue{var doubleValue:Double{get{return value}}}



public func __isStringNumber(value:Any)->(Bool,Double){
    if let v = value as? String
    {
        if let doubleV = Double(v)
        {
            return (true,doubleV)
        }
        return (false,0)
    }
    return (false,0)
}

